/*
 * ExplicaCoreView.java
 *
 * $Id: ExplicaCoreView.java,v 1.9 2009-10-27 10:09:06 mario Exp $
 */
package org.ceteca.explica.client.gui;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import org.ceteca.explica.client.ClientContext;
import org.ceteca.explica.core.InterfaceExplica;
import org.ceteca.explica.core.installer.AreaOT;
import org.ceteca.explica.core.installer.ModuleLauncher;
import org.ceteca.explica.core.installer.ResourceOT;
import org.ceteca.javaglade.Gui;
import org.ceteca.javaglade.InterfaceGladeGtk;
import org.ceteca.javaglade.JavaGladeException;
import org.ceteca.javaglade.JavaGladeUtil;
import org.ceteca.javaglade.handlers.GenericHandler;
import org.ceteca.javaglade.toolkit.swt.SwtTopLevelWindow;
import org.ceteca.javaglade.toolkit.swt.SwtWidget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Widget;

/**
 * Helper class that implements the Explica core GUI. It completes the glade
 * file specification to add extra features. It also implements the event
 * handlers for the Explica main screens and dialogs. Manages the login of the
 * user and introduces the main window with the modules and operations available
 * for its use. <br/> Responsibilities:
 * <ul>
 * <li>Definition and inicialization of the Explica core GUI. </li>
 * <li>Event management for the Explica main windows.</li>
 * </ul>
 * <br/>
 * 
 * @author Mario García García <mario@imagos.es> Copyright (c) 2008 Fundación
 *         Centro Tecnolóxico da Carne This program is free software: you can
 *         redistribute it and/or modify it under the terms of the GNU General
 *         Public License as published by the Free Software Foundation, either
 *         version 3 of the License, or (at your option) any later version.
 *         <br/> This program is distributed in the hope that it will be useful,
 *         but WITHOUT ANY WARRANTY; without even the implied warranty of
 *         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *         General Public License for more details. <br/> You should have
 *         received a copy of the GNU General Public License along with this
 *         program. If not, see <http://www.gnu.org/licenses/>.
 */
public class ExplicaCoreView extends GenericHandler {
	/** Catalog of areas (indexed by their corresponding name) */
	private Hashtable<String, AreaOT> areas;
	/** Catalog of areas (ordered by position as shown in the combo) */
	private ArrayList<AreaOT> lAreas;

	/** Catalog of installed modules (indexed by their corresponding name) */
	private Hashtable<String, ModuleLauncher> modulesByName;

	/**
	 * Catalog of installed modules (indexed by their corresponding button
	 * launcher widget)
	 */
	private Hashtable<Widget, ModuleLauncher> modules;

	/** Unique instance of the TraceabilityTreeView class. Singleton pattern */
	private static ExplicaCoreView instance;

	/**
	 * Returns the unique instance of the TraceabilityTreeView class. Implements
	 * the singleton dessign pattern.
	 * 
	 * @return TraceabilityTreeView, unique instance of TraceabilityTreeView.
	 */
	public static final ExplicaCoreView getInstance() {
		if (instance == null)
			instance = new ExplicaCoreView();
		return instance;
	}

	/**
	 * Private default constructor por TraceabilityTreeView objects.
	 */
	private ExplicaCoreView() {
		ClientContext.getInstance().getLogger().info(
				"Building the main view of the application...");
		this.areas = new Hashtable<String, AreaOT>();
		this.lAreas = new ArrayList<AreaOT>();
		this.modules = new Hashtable<Widget, ModuleLauncher>();

		// Init the logging system for the JavaGlade module
		ClientContext
				.getInstance()
				.getLogger()
				.debug(
						"Initializing the logging system of the JavaGlade. Configuration file found at: "
								+ this.getClass().getResource(
										"/org/ceteca/javaglade/log4j.xml")
										.getPath());
		JavaGladeUtil.getInstance().initLogger(ExplicaGUIBuilder.getInstance(), "logs", "/org/ceteca/javaglade/log4j.xml");
	}

	/**
	 * Gets the Explica main areas' catalog (indexed by name of area). Each area
	 * contains a set of modules that implements the tools needed for the
	 * managing of that area.
	 * 
	 * @return Hashtable<String, AreaOT>, a catalog of areas.
	 */
	public Hashtable<String, AreaOT> getAreas() {
		return this.areas;
	}

	/**
	 * Sets up the Explica main areas' catalog (indexed by name of area). Each
	 * area contains a set of modules that implements the tools needed for the
	 * managing of that area.
	 * 
	 * @param items
	 *            Hashtable<String, AreaOT>, a catalog of areas.
	 */
	public void setAreas(Hashtable<String, AreaOT> areas) {
		this.areas = areas;
	}

	/**
	 * Initializes the Explica Core Viewer engine and builds the main window of
	 * the application. Searches automatically for the glade interface file with
	 * the application GUI configuration.
	 */
	public void buildCoreView() {
		// Build the main window content
		Gui gui = null;
		try {
			gui = ExplicaGUIBuilder.getInstance().buildGUI(InterfaceExplica.EXPLICA_CORE_GLADE_NAME, InterfaceExplica.EXPLICA_CORE_GLADE_PATH);
		} 
		catch (JavaGladeException jEx) {
			ClientContext.getInstance().getLogger().error(
				"Error constructing the JavaGlade from the glade main screen file!", jEx);
		} 
		catch (Exception ex) {
			ClientContext.getInstance().getLogger().error(
				"Error constructing the JavaGlade from the glade main screen file!", ex);
		}

		if (gui != null) {
			// Load the areas combobox
			this.loadExplicaAreas();
			
			// Set the background colors for both sides of the sash form
			Composite rightPanel = (Composite)gui.getWidget(InterfaceExplica.EXPLICA_MODULE_CONTENTS_PANEL_NAME);
			rightPanel.setBackground(ExplicaGUIBuilder.getInstance().getColor(SWT.COLOR_DARK_GRAY));
			Composite leftPanel = (Composite)gui.getWidget(InterfaceExplica.EXPLICA_MAIN_LEFT_PANEL_NAME);
			leftPanel.setBackground(ExplicaGUIBuilder.getInstance().getColor(SWT.COLOR_DARK_GRAY));
	
			SashForm sash = (SashForm)gui.getWidget("hpanedExplicaMain");
			int[] weights = { 1, 4 };
			sash.setWeights(weights);
	
			// Configure the panel where the module buttons of the area will be placed
			this.configureModulesPanel();
	
//			ExplicaGUIBuilder.getInstance().printWidgets(InterfaceExplica.EXPLICA_CORE_GLADE);
	
			ClientContext.getInstance().getLogger().info(
					"Starting the execution of the main screen of the application...");
	
			// Maximize the main window
			SwtTopLevelWindow mainWindow = gui.getWindow(InterfaceExplica.EXPLICA_MAIN_WINDOW_NAME);
			mainWindow.getShell().setMaximized(true);
			
			// TO_DO: Remove the auto-selection invoicing area when all the areas are
			// supported.
			// Auto-select by default the Invoicing Area
			Combo comboAreas = this.getComboAreas();
			comboAreas.select(0);
			// Get the panel where the modules of the area will be inserted
/*			SwtWidget panelModules = this.getModulesPanel();
			String selAreaName = comboAreas.getItem(comboAreas.getSelectionIndex());
			ClientContext.getInstance().getLogger().info("Auto-selecting area: " + selAreaName);
			AreaOT selArea = this.lAreas.get(comboAreas.getSelectionIndex());
			ClientContext.getInstance().getLogger().info("Selected area data: " + selArea);
			this.buildAreaPanel(gui, selArea, panelModules);
*/
	
			// Auto-select by default the Warehouse Module
			ModuleLauncher modLauncher = this.modulesByName.get("warehouseModule");
	
			// Start the module
			if (modLauncher != null) {
				// Set up the parent javaGlade instance
				modLauncher.startModule();
			} else {
				ClientContext.getInstance().getLogger().error(
						String.format(InterfaceExplica.EXPLICA_LOG_MODULE_NOT_FOUND,
								"warehouseModule"));
			}
			
			ToolBar toolbar = (ToolBar)this.getGui().getWidget(InterfaceExplica.EXPLICA_MODULE_ENTITIES_TOOLBAR_NAME);
			// Configure the entities toolbar to auto-scroll
			JavaGladeUtil.getInstance().getSwtToolkit().connectSignal(
					InterfaceGladeGtk.GTK_SIGNAL_TYPE_SIZEREQUEST, 
					toolbar, "on_toolItem_resized", this);
		}
	}

	/**
	 * Sets up the list of installed modules available for the user.
	 * 
	 * @param modules Hashtable<String, ModuleLauncher>, list of installed modules.
	 */
	public void setInstalledModules(Hashtable<String, ModuleLauncher> modules) {
		StringBuffer buf = new StringBuffer();
		buf.append("Setting installed modules list:  ");
		buf.append(modules.size());

		Iterator it = modules.keySet().iterator();
		while (it != null && it.hasNext()) {
			buf.append("\n\t\tMODULE: ");
			buf.append(it.next());
		} // for

		ClientContext.getInstance().getLogger().info(buf.toString());

		this.modulesByName = modules;
	}

	/**
	 * Gets the list of installed modules available for the user.
	 * 
	 * @return Hashtable<String, ModuleLauncher>, list of installed modules.
	 */
	public Hashtable<String, ModuleLauncher> getInstalledModules() {
		return this.modulesByName;
	}

	/**
	 * Builds the area panel that will contain the list of installed modules for
	 * each area.
	 * 
	 * @param parentGlade JavaGlade, JavaGlade instance used in the construction of the
	 *            Explica main window.
	 * @param areaOT ValueOT, data of the area the panel is built for.
	 * @param rootComposite Composite, composite widget that will hold the panel of the
	 *            area.
	 */
	private void buildAreaPanel(Gui gui, AreaOT areaOT, SwtWidget parentWidget) {
		ClientContext.getInstance().getLogger().info("Building area panel ...");

		if (areaOT != null) {
			ClientContext.getInstance().getLogger().info(
					"Building area panel " + areaOT.getIdArea() + " - "
							+ areaOT.getDescription());
			// Build the list of available modules for the area
			for (int i = 0; areaOT.getLModules() != null
					&& i < areaOT.getLModules().size(); i++) {
				ResourceOT modOT = (ResourceOT) areaOT.getLModules().get(i);

				// Build the module button
				Button buttonMod = this.buildModuleButton(modOT, gui, parentWidget);

				// Update the catalog of modules with the new created button
				// module
				ClientContext.getInstance().getLogger().debug(
						String.format("Module %s added to the catalog: %s",
								modOT.getCodResource(), this.modulesByName.get(modOT.getCodResource())));
				this.modules.put(buttonMod, this.modulesByName.get(modOT.getCodResource()));
			} // for each module of the area
			
			// Redraw the root composite
			Composite rootComposite = (Composite)parentWidget.getWidget();
			rootComposite.setSize(rootComposite.computeSize(SWT.DEFAULT,
					SWT.DEFAULT));

//			ClientContext.getInstance().getLogger().debug("########  Widgets after adding module buttons: ");
//			parentGlade.printWidgets();

			rootComposite.getParent().layout(true);
		} 
		else {
			ClientContext.getInstance().getLogger().error(
					InterfaceExplica.EXPLICA_LOG_ERROR_GETTING_MODULE);
		}
	}

	/**
	 * Handler for the module button click event. Inits the module and shows the
	 * module main window on the screen.
	 * 
	 * @param e Event, module button clicked event data.
	 */
	public void on_moduleButton_clicked(Event e) {
		// Get the button widget that raised the event
		Button modButton = (Button) e.widget;
		ClientContext.getInstance().getLogger().info(
				String.format("Button pressed: %s", modButton.getText()));

		// Get the module launcher corresponding to the pressed button module
		ModuleLauncher modLauncher = this.modules.get(modButton);
		ClientContext.getInstance().getLogger().debug(
				String.format("Module launcher located: %s", modLauncher));

		// Start the module
		if (modLauncher != null) {
			ClientContext.getInstance().getLogger().debug(
					String.format("Starting module: %s", modButton.getText()));
			modLauncher.startModule();
		} 
		else {
			ClientContext.getInstance().getLogger().error(
					String.format(InterfaceExplica.EXPLICA_LOG_MODULE_NOT_FOUND,
							modButton.getText()));
		}
	}

	/**
	 * Handler for the areas combo selection changed event. Builds the module
	 * button bar for the selected area.
	 * 
	 * @param e Event, areas combo changed event data.
	 */
	public void on_areaCombo_changed(Event e) {
		Gui gui = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME);
		Combo comboAreas = (Combo) e.widget;

		// Get the panel where the modules of the area will be inserted
		SwtWidget widgetModulesMenu = gui.getSwtWidget(InterfaceExplica.EXPLICA_MODULES_PANEL_NAME);
		// Build the panel for the selected area
		if (comboAreas != null) {
			String selAreaName = comboAreas.getItem(comboAreas.getSelectionIndex());
			ClientContext.getInstance().getLogger().info(
					String.format("Area combo selection changed: %s - %s",
							comboAreas.getSelectionIndex(), selAreaName));

//			AreaOT selArea = this.areas.get(selAreaName);
			AreaOT selArea = this.lAreas.get(comboAreas.getSelectionIndex());
			if (selArea != null) {
				this.buildAreaPanel(ExplicaGUIBuilder.getInstance().getGUI(InterfaceExplica.EXPLICA_CORE_GLADE_NAME), selArea, widgetModulesMenu);
			} // if selected area is valid
		}
	}

	/**
	 * Adds the area to the combo of the main screen.
	 * 
	 * @param areaOT ValueOT, data of the area that will be added.
	 * @param comboAreas Combo, Combo widget that will hold all the areas defined for
	 *            the application.
	 */
	private void addArea(AreaOT areaOT, Combo comboAreas) {
		ClientContext.getInstance().getLogger().info(
				String.format("Area %s added to the areas combo!", areaOT.getDescription()));
		comboAreas.add(ClientContext.getInstance().getI18n().tr(areaOT.getDescription()));
	}

	/**
	 * Handler for the Exit button click event. Closes the application.
	 * 
	 * @param e Event, exit button clicked event data.
	 */
	public void on_buttonExplicaExit_clicked(Event e) {
		SwtTopLevelWindow mainWindow = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME).
				getWindow(InterfaceExplica.EXPLICA_MAIN_WINDOW_NAME);
		ClientContext.getInstance().getLogger().info("Closing Explica application... " + mainWindow);
		if (mainWindow != null)
			mainWindow.getShell().close();
	}
	
	/**
	 * Handler for the About button click event. Shows the about information dialog.
	 * 
	 * @param e Event, about button clicked event data.
	 */
	public void on_buttonExplicaAbout_clicked(Event e) {
		SwtTopLevelWindow mainWindow = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME).
				getWindow(InterfaceExplica.EXPLICA_MAIN_WINDOW_NAME);
		
		ExplicaGUIBuilder.getInstance().showConfirmationDialog(mainWindow.getShell(), 
				"EXPLICA\nEntorno de Xestión da Produción Libre para Industrias Cárnicas\n\n" +
				"Copyright (c) 2007,2008,2009 Centro Tecnolóxico da Carne\n" + 
				"http://ceteca.net/explica/\n\n" + 
				"Autores e equipo técnico:\n\n" +
				"Mario García García, Juan Carlos Díaz López, Jose Manuel Lorenzo Rodríguez, Rubén Rodríguez Pérez.", 
				"Explica v0.3 About", 
				SWT.ICON_INFORMATION, SWT.CLOSE);
	}
	
	/**
	 * (Re)Loads the combo with the available Explica Areas.
	 */
	public void loadExplicaAreas() {
		this.lAreas = ClientContext.getInstance().getAreasCatalog();

		// Get the combobox to fill it with all the areas defined for the
		// application
		Combo comboAreas = this.getComboAreas();
		
		// Fill the combo with the available areas
		for (int i = 0; lAreas != null && i < lAreas.size(); i++) {
			this.addArea((AreaOT) lAreas.get(i), comboAreas);
			this.areas.put(((AreaOT) lAreas.get(i)).getDescription(),
					(AreaOT) lAreas.get(i));
		} // for each area

		// Add a listener for the selection event of the combo
		ExplicaGUIBuilder.getInstance().getToolkit().connectSignal(InterfaceGladeGtk.GTK_SIGNAL_TYPE_CHANGED,
				comboAreas,
				InterfaceExplica.EXPLICA_AREA_COMBO_CHANGED_HANDLER, this);
		ClientContext.getInstance().getLogger().debug(
				"Areas combobox selection changed event connected to its handler!");
	}
	
	/**
	 * Clears the combo of Areas. Removes all the explica Areas from the combo.
	 */
	public void clearExplicaAreas() {
		Combo comboAreas = this.getComboAreas();
		if (comboAreas != null) {
			comboAreas.removeAll();
			this.lAreas.clear();
			this.areas.clear();
		}
	}
	
	/**
	 * Removes all the selected area's module buttons.
	 */
	public void clearExplicaModules() {
		SwtWidget modulesWidget = this.getModulesPanel();
		Composite panelModules = (Composite)modulesWidget.getWidget();
		if (panelModules != null) {
			panelModules.dispose();
			this.buildModulesPanel();
		}
	}
	
	/**
	 * Gets the combo widget that will hold all the Explica Areas.
	 * @return Combo, areas combo widget.
	 */
	private Combo getComboAreas() {
		Gui gui = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME);
		Combo comboAreas = (Combo)gui.getWidget(InterfaceExplica.EXPLICA_AREAS_COMBO_NAME);
		
		ClientContext.getInstance().getLogger().debug(
				String.format("Areas combobox %s found!", InterfaceExplica.EXPLICA_AREAS_COMBO_NAME));
		
		return comboAreas;
	}
	
	/**
	 * Get the panel where the module buttons of the selected area will be placed.
	 * @return SwtWidget, module panel.
	 */
	private SwtWidget getModulesPanel() {
		Gui gui = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME);
		SwtWidget modulesPanel = gui.getSwtWidget(InterfaceExplica.EXPLICA_MODULE_PANEL_NAME);
		ClientContext.getInstance().getLogger().debug(
				String.format("Module buttons' panel %s found!",
						InterfaceExplica.EXPLICA_MODULE_PANEL_NAME));
		
		return modulesPanel;
	}
	
	private void buildModulesPanel() {
		Gui gui = ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME);
		SwtWidget parent = gui.getSwtWidget(InterfaceExplica.EXPLICA_AREA_PANEL_NAME);
		Composite modulesPanel = (Composite)ExplicaGUIBuilder.getInstance().getToolkit().emptyPanel((Composite)parent.getWidget(), InterfaceExplica.EXPLICA_MODULE_PANEL_NAME);
		gui.addWidget(InterfaceExplica.EXPLICA_MODULE_PANEL_NAME, modulesPanel, parent.getWidgetName());
		
		this.configureModulesPanel();
	}
	
	private void configureModulesPanel() {
		// Get the panel where the module buttons of the area will be placed
		SwtWidget modulesWidget = this.getModulesPanel();
		Composite modulesPanel = (Composite)modulesWidget.getWidget();
		// Set up the modules panel layout to GridLayout
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		modulesPanel.setLayout(layout);
		// Set up the modules panel layout data to fill the parent's area
		Object data = modulesPanel.getLayoutData();
		((GridData)data).horizontalAlignment = SWT.FILL;
		((GridData)data).grabExcessHorizontalSpace = true;
		modulesPanel.setLayoutData(data);
	}
	
	private Button buildModuleButton(ResourceOT modOT, Gui gui, SwtWidget rootWidget) {
		Composite rootComposite = (Composite)rootWidget.getWidget();
		// Build the button that will launch the module
		Button buttonMod = (Button) ExplicaGUIBuilder.getInstance().getToolkit().button(rootComposite,
				"EXPLICA_MODULE_" + modOT.getCodResource() + "_BUTTON",
				ClientContext.getInstance().getI18n().tr(modOT.getDescription()));
		gui.addWidget(
				InterfaceExplica.EXPLICA_MODULE_BUTTON_NAME + "_" + modOT.getCodResource(), 
				buttonMod,
				rootWidget.getWidgetName());
		ClientContext.getInstance().getLogger().debug(
				String.format("Module %s button built!", "EXPLICA_MODULE_" + ClientContext.getInstance().getI18n().tr(modOT.getDescription())));

		// Set the module's button icon image
		String iconPath = "";
		try {
			ClientContext.getInstance().getLogger().debug(
					String.format("Module %s icon stream found: %s = %s", "EXPLICA_MODULE_" + modOT.getDescription(),
							ClientContext.getInstance().getResourceDir(),
							JavaGladeUtil.getInstance().getResource(ClientContext.getInstance().getResourceDir() + "/icon.gif")));
			
			System.out.println(JavaGladeUtil.getInstance().getResource("/resources/img/icon.gif"));
			Image image = new Image(ExplicaGUIBuilder.getInstance().getDisplay(),
					JavaGladeUtil.getInstance().getResource("/resources/img/icon.gif"));
			System.out.println("IMAGE GOT: " + image);
			ClientContext.getInstance().getLogger().debug("IMAGE GOT: " + image);
			buttonMod.setImage(image);
			ClientContext.getInstance().getLogger().debug(
					String.format("Module %s icon setted up: ", "EXPLICA_MODULE_" + modOT.getDescription()));
			ClientContext.getInstance().getLogger().debug(
					String.format("Size of the button's image: (%d, %d)",
							image.getBounds().width,
							image.getBounds().height));
			buttonMod.setSize(buttonMod.computeSize(SWT.DEFAULT, image.getBounds().height));
			buttonMod.setData(gui);
		} 
		catch (Exception ex) {
			ClientContext.getInstance().getLogger().error(
					String.format(InterfaceExplica.EXPLICA_LOG_ERROR_GETTING_MODULE_ICON, iconPath), 
					ex);
		}

		// Set the layout data for the module button
		
		if (rootComposite.getLayout() instanceof GridLayout) {
			GridData layoutData = new GridData(SWT.LEFT, SWT.TOP,
					false, false);
			layoutData.grabExcessHorizontalSpace = true;
			layoutData.horizontalAlignment = SWT.FILL;
			layoutData.grabExcessVerticalSpace = true;
			layoutData.verticalAlignment = SWT.FILL;
			buttonMod.setLayoutData(layoutData);
		}
		
		// Connect the clicked signal to the button
		ExplicaGUIBuilder.getInstance().getToolkit().connectSignal(
				InterfaceGladeGtk.GTK_SIGNAL_TYPE_CLICKED, buttonMod,
				InterfaceExplica.EXPLICA_MODULE_BUTTON_CLICKED_HANDLER,
				this);
		
		return buttonMod;
	}

	/**
	 * Returns tag Id assigned to CVS source file.
	 */
	public static String getRevision() {
		return "$Id: ExplicaCoreView.java,v 1.9 2009-10-27 10:09:06 mario Exp $";
	}
}